package com.san.park;

import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * This class should be singleton for each parking lot. So this may be used in spring or in Java after a little modification
 * for singleton. Also a servlet may be designed to feature this functionality.
 * 
 * 
 * Before a car gets entry "placeACar()" must be called. If returned true then only the car may enter through the Gate
 * Before a car is released through the Gate, "vacateByCar()" must be called
 * The sequence is as follows
 * 1> A car appears in the Gate from outside for parking
 * 2> The Computer System at the different gates or the same gate where the car is waiting, calls the "placeACar()" method
 * 3> The Gate opens and the car gets a parking
 * 4> The car now comes to any gate to get out of the parking lot.
 * 5> "vacateByCar()" method is called
 * 6> The Gate opens and the car departs from the Parking lot.
 * @author sankar.banerjee
 *
 */
public class ParkingLot {

	/**
	 * Key is the type of the car/position
	 */
	private ConcurrentHashMap<Integer,Car> cars = new ConcurrentHashMap<Integer,Car>();
	private HashMap<Integer,ConcurrentLinkedQueue<Position>> emptyPositions;
	/**
	 * Key is the unique number identifying the position
	 */
	private ConcurrentLinkedQueue<Position> filledAlready;
	
	
	public HashMap<Integer,ConcurrentLinkedQueue<Position>> getLot() {
		return emptyPositions;
	}
	
	/**
	 * Initialize
	 * @param lot
	 */
	public void setLot(HashMap<Integer, ConcurrentLinkedQueue<Position>> lot) {
		this.emptyPositions = lot;
	}
	
	/**
	 * This method takes care of the multithreaded nature of calls.
	 * Multiple threads may call this method at the same time even with the same car object. Due to this
	 * special code has been designed.
	 * @param car
	 * @return
	 */
	public boolean placeACar(Car car){
		Car present = cars.putIfAbsent(car.getUniqueRegistrationNo(), car);
		/**
		 * If this is a duplicate parking request with the same car which is already in the parking lot
		 * return false. If there is no likeliness for this to happen, the if condition may be omitted from code. 
		 */
		if(present!=null){
			return false;
		}
		/**
		 * Threadsafe queue
		 */
		ConcurrentLinkedQueue<Position> getTypeOfPositions =  emptyPositions.get(car.getType());
		/**
		 * Atomic operation. Separate threads always get a distinct position.
		 */
		Position pos = getTypeOfPositions.poll();
		/**
		 * No empty position found
		 */
		if(pos==null){
			cars.remove(car);
			return false;
		}
		pos.setFilledIn(car);
		filledAlready.add(pos);		
		car.setPosition(pos);
		return true;
		
		
	}
	
	/**
	 * Considering that this method may be called by multiple threads even for a single car
	 * special code is designed
	 * @param regNo
	 * @return
	 */
	public boolean vacateByCar(int regNo){
		
		Car car = cars.remove(regNo);
		/**
		 * If this is a duplicate request return false or the car was never there inside the lot
		 * If there is no likeliness for this to happen, the if condition may be omitted from code.
		 */
		if(car == null)
			return false;
		ConcurrentLinkedQueue<Position> getTypeOfPositions =  emptyPositions.get(car.getType());
		Position position = car.getPosition();
		
		if(position!=null){
			filledAlready.remove(position);
			position.free();
			/**
			 * Make the position available for other cars
			 */
			getTypeOfPositions.add(position);
			cars.remove(regNo);
			return true;
		}
		
		return false;
	}

}
